package cn.cdutetc.library.service.impl;

import cn.cdutetc.library.entity.pojo.User;
import cn.cdutetc.library.mapper.NotificationMapper;
import cn.cdutetc.library.mapper.UserMapper;
import cn.cdutetc.library.service.NotificationService;
import cn.hutool.core.bean.BeanUtil;
import cn.cdutetc.library.entity.dto.NotificationDTO;
import cn.cdutetc.library.entity.dto.NotificationVO;
import cn.cdutetc.library.entity.enums.NotificationTypeEnum;
import cn.cdutetc.library.entity.pojo.Notification;
import cn.cdutetc.library.common.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 通知服务实现类
 */
@Service
public class NotificationServiceImpl extends ServiceImpl<NotificationMapper, Notification> implements NotificationService {

    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer createNotification(NotificationDTO notificationDTO) {
        // 检查用户是否存在
        if (notificationDTO.getUserId() != null) {
            User user = userMapper.selectById(notificationDTO.getUserId());
            if (user == null) {
                throw new BizException("用户不存在");
            }
        }

        // 检查通知类型是否有效
        String dbType = mapTypeCodeToDb(notificationDTO.getType());
        if (!isValidNotifyType(dbType)) {
            throw new BizException("无效的通知类型: " + notificationDTO.getType() + "，映射到数据库的值: " + dbType);
        }

        // 创建通知
        Notification notification = new Notification();
        BeanUtil.copyProperties(notificationDTO, notification);
        // 类型：前端传 code(1/2/3/4/5)，库为字符串（due_reminder/reservation_notify/announcement等）
        System.out.println("映射通知类型: " + notificationDTO.getType() + " -> " + dbType);
        notification.setNotifyType(dbType);
        notification.setNotifyWay(notificationDTO.getNotifyWay() != null ? notificationDTO.getNotifyWay() : "system");
        notification.setSendTime(LocalDateTime.now());
        notification.setReadStatus("unread");

        try {
            this.save(notification);
            return notification.getNotificationId();
        } catch (Exception e) {
            // 记录详细的错误日志
            System.err.println("创建通知失败: " + e.getMessage());
            e.printStackTrace();
            throw new BizException("创建通知失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean createSystemNotification(NotificationDTO notificationDTO) {
        // 查询所有用户
        List<User> users = userMapper.selectList(null);
        if (users.isEmpty()) {
            return false;
        }

        // 检查通知类型是否有效
        String dbType = mapTypeCodeToDb(notificationDTO.getType());
        if (!isValidNotifyType(dbType)) {
            throw new BizException("无效的通知类型: " + notificationDTO.getType() + "，映射到数据库的值: " + dbType);
        }
        
        // 为每个用户创建通知
        List<Notification> notifications = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();

        for (User user : users) {
            Notification notification = new Notification();
            BeanUtil.copyProperties(notificationDTO, notification);
            notification.setUserId(user.getUserId());
            notification.setNotifyType(dbType);
            notification.setNotifyWay(notificationDTO.getNotifyWay() != null ? notificationDTO.getNotifyWay() : "system");
            notification.setSendTime(now);
            notification.setReadStatus("unread");
            notifications.add(notification);
        }

        return this.saveBatch(notifications);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markAsRead(Integer notificationId) {
        // 检查通知是否存在
        Notification notification = this.getById(notificationId);
        if (notification == null) {
            throw new BizException("通知不存在");
        }

        // 如果已读，直接返回成功
        if ("read".equalsIgnoreCase(notification.getReadStatus())) {
            return true;
        }

        // 标记为已读
        notification.setReadStatus("read");
        notification.setReadTime(LocalDateTime.now());

        return this.updateById(notification);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchMarkAsRead(List<Integer> notificationIds) {
        if (notificationIds == null || notificationIds.isEmpty()) {
            return false;
        }

        // 查询通知
        List<Notification> notifications = this.listByIds(notificationIds);
        if (notifications.isEmpty()) {
            return false;
        }

        // 标记为已读
        LocalDateTime now = LocalDateTime.now();
        notifications.forEach(notification -> {
            notification.setReadStatus("read");
            notification.setReadTime(now);
        });

        return this.updateBatchById(notifications);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markAllAsRead(Integer userId) {
        // 检查用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BizException("用户不存在");
        }

        // 查询用户未读通知
        LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Notification::getUserId, userId)
                .eq(Notification::getReadStatus, "unread");
        List<Notification> notifications = this.list(queryWrapper);

        if (notifications.isEmpty()) {
            return true;
        }

        // 标记为已读
        LocalDateTime now = LocalDateTime.now();
        notifications.forEach(notification -> {
            notification.setReadStatus("read");
            notification.setReadTime(now);
        });

        return this.updateBatchById(notifications);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteNotification(Integer notificationId) {
        // 检查通知是否存在
        Notification notification = this.getById(notificationId);
        if (notification == null) {
            throw new BizException("通知不存在");
        }

        return this.removeById(notificationId);
    }

    @Override
    public NotificationVO getNotificationById(Integer notificationId) {
        // 查询通知
        Notification notification = this.getById(notificationId);
        if (notification == null) {
            throw new BizException("通知不存在");
        }

        // 转换为VO
        return convertToVO(notification);
    }

    @Override
    public IPage<NotificationVO> pageUserNotifications(Integer page, Integer size, Integer userId, Integer type, Boolean isRead) {
        // 构建查询条件
        LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
        if (userId != null) {
            queryWrapper.eq(Notification::getUserId, userId);
        }
        if (type != null) {
            String dbType = mapTypeCodeToDb(type);
            if (dbType != null) {
                queryWrapper.eq(Notification::getNotifyType, dbType);
            }
        }
        if (isRead != null) {
            queryWrapper.eq(Notification::getReadStatus, isRead ? "read" : "unread");
        }
        queryWrapper.orderByDesc(Notification::getSendTime);

        // 分页查询
        Page<Notification> notificationPage = new Page<>(page, size);
        Page<Notification> resultPage = this.page(notificationPage, queryWrapper);

        // 转换为VO
        List<NotificationVO> notificationVOList = resultPage.getRecords().stream()
                .map(this::convertToVO)
                .filter(Objects::nonNull)  // 过滤掉空对象
                .collect(Collectors.toList());

        // 构建返回结果
        Page<NotificationVO> voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal());
        voPage.setRecords(notificationVOList);

        return voPage;
    }

    @Override
    public Integer countUnreadNotifications(Integer userId) {
        // 检查用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BizException("用户不存在");
        }

        // 查询未读通知数量
        LambdaQueryWrapper<Notification> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Notification::getUserId, userId)
                .eq(Notification::getReadStatus, "unread");

        return Math.toIntExact(this.count(queryWrapper));
    }

    @Override
    public Integer createBorrowNotification(Integer userId, Integer borrowId, Integer type, String title, String content) {
        try {
            // 检查用户是否存在
            User user = userMapper.selectById(userId);
            if (user == null) {
                System.err.println("创建通知失败: 用户不存在");
                return null;
            }

            // 使用统一的映射方法
            String notifyType = mapTypeCodeToDb(type);
            if (notifyType == null) {
                System.err.println("创建通知失败: 无效的通知类型 " + type);
                return null;
            }

            // 验证通知类型是否有效
            if (!isValidNotifyType(notifyType)) {
                System.err.println("创建通知失败: 无效的通知类型 " + notifyType);
                return null;
            }

            // 创建通知
            Notification notification = new Notification();
            notification.setUserId(userId);
            notification.setNotifyType(notifyType);  // 直接使用数据库中定义的枚举值
            notification.setTitle(title);
            notification.setContent(content);
            notification.setNotifyWay("system");
            notification.setSendTime(LocalDateTime.now());
            notification.setReadStatus("unread");

            System.out.println("准备保存的通知对象: " + notification);
            System.out.println("通知类型: " + notifyType);

            this.save(notification);
            return notification.getNotificationId();
        } catch (Exception e) {
            // 记录详细异常信息，但不抛出，避免影响主业务流程
            System.err.println("创建借阅通知失败: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 验证通知类型是否为数据库中定义的有效值
     * @param notifyType 通知类型
     * @return 是否有效
     */
    private boolean isValidNotifyType(String notifyType) {
        if (notifyType == null) return false;
        
        // 数据库中定义的有效通知类型
        switch (notifyType) {
            case "due_reminder":
            case "reservation_notify":
            case "announcement":
            case "borrow_notify":
            case "return_notify":
            case "overdue_notify":
            case "fine_notify":
            case "borrow_reminder":
            case "return_reminder":
                return true;
            default:
                System.out.println("未知的通知类型: " + notifyType);
                return false;
        }
    }

    /**
     * 将Notification实体转换为NotificationVO
     * @param notification 通知实体
     * @return 通知VO
     */
    private NotificationVO convertToVO(Notification notification) {
        if (notification == null) {
            return null;
        }
        NotificationVO notificationVO = new NotificationVO();
        BeanUtil.copyProperties(notification, notificationVO);
        
        // 设置通知ID
        notificationVO.setNotificationId(notification.getNotificationId());

        // 查询用户信息
        if (notification.getUserId() != null) {
            User user = userMapper.selectById(notification.getUserId());
            if (user != null) {
                notificationVO.setUsername(user.getUsername());
                notificationVO.setRealName(user.getRealName());
            }
        }

        // 设置类型描述与 code
        Integer typeCode = mapDbTypeToCode(notification.getNotifyType());
        notificationVO.setType(typeCode);
        if (typeCode != null) {
            switch (typeCode) {
                case 1:
                    notificationVO.setTypeDesc("系统通知");
                    break;
                case 2:
                    notificationVO.setTypeDesc("借阅提醒");
                    break;
                case 3:
                    notificationVO.setTypeDesc("还书提醒");
                    break;
                case 4:
                    notificationVO.setTypeDesc("逾期提醒");
                    break;
                case 5:
                    notificationVO.setTypeDesc("罚款通知");
                    break;
                default:
                    notificationVO.setTypeDesc("未知类型");
            }
        } else {
            // 如果无法映射类型代码，则使用默认值
            notificationVO.setType(1);
            notificationVO.setTypeDesc("系统通知");
        }

        // 确保类型描述不为空
        if (notificationVO.getTypeDesc() == null || notificationVO.getTypeDesc().isEmpty()) {
            notificationVO.setTypeDesc("未知类型");
        }

        return notificationVO;
    }

    /**
     * 将前端 code(1/2/3/4/5) 映射为库中的字符串枚举
     */
    private String mapTypeCodeToDb(Integer code) {
        if (code == null) return null;
        switch (code) {
            case 1: return "announcement";      // 系统通知
            case 2: return "borrow_notify";     // 借阅提醒
            case 3: return "return_notify";     // 还书提醒
            case 4: return "overdue_notify";    // 逾期提醒
            case 5: return "fine_notify";       // 罚款通知
            default: 
                System.err.println("警告: 未知的通知类型代码: " + code + "，使用默认值 'announcement'");
                return "announcement";
        }
    }

    /**
     * 将库中的字符串枚举映射回前端 code
     */
    private Integer mapDbTypeToCode(String t) {
        if (t == null) return null;
        switch (t) {
            case "announcement": return 1;
            case "borrow_notify": return 2;
            case "return_notify": return 3;
            case "overdue_notify": return 4;
            case "fine_notify": return 5;
            // 兼容旧的数据库值
            case "due_reminder": return 4;      // 逾期提醒
            case "reservation_notify": return 2; // 借阅提醒
            case "borrow_reminder": return 2;   // 借阅提醒
            case "return_reminder": return 3;   // 还书提醒
            default: 
                System.err.println("警告: 未知的通知类型字符串: " + t + "，使用默认值 1");
                return 1;
        }
    }
}